package com.weidey.blogWeb.service.Impl;

import com.weidey.blog.domain.Article;
import com.weidey.blog.domain.Label;
import com.weidey.blog.service.ILabelService;
import com.weidey.blogWeb.coer.UserCF;
import com.weidey.blogWeb.domain.WebArticle;
import com.weidey.blogWeb.domain.WebRating;
import com.weidey.blogWeb.domain.dto.RelateDTO;
import com.weidey.blogWeb.mapper.WebArticleMapper;
import com.weidey.blogWeb.mapper.WebLIkeMapper;
import com.weidey.blogWeb.service.ILikeService;
import com.weidey.blogWeb.service.IWebArticleService;
import com.weidey.blogWeb.service.IWebRatingService;
import com.weidey.common.core.domain.AjaxResult;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.Random;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
public class WebArticleServiceImpl implements IWebArticleService {

    @Resource
    IWebRatingService blogWebRatingService;

    @Resource
    WebArticleMapper webArticleMapper;

    @Resource
    ILikeService blogLikeService;

    @Resource
    ILabelService blogLabelService;

    @Resource
    WebLIkeMapper webLIkeMapper;

    // 注入redis RedisTemplate
    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Override
    public List<WebArticle> userCfRecommend(long userId) {
        //读取评分数据  放进关联模型里
        List<WebRating> temp = blogWebRatingService.selectBlogRatingList(new WebRating());
        //读取文章数据
        List<WebArticle> articles = selectBlogArticleList();
        //当数据过少 或 用户没有评分数据时随机推荐
        if (temp.size() < 3 || temp.stream().noneMatch(t -> t.getUserId().equals(userId))) {
            return getRecommendedOrRandomArticles(articles, null);
        }
        //计算相似度
        List<RelateDTO> data = createRating(temp);
        //把用户参数用户id 与 关联模型传入
        List<Integer> recommendations = UserCF.recommend(Long.valueOf(userId).intValue(), data);
        // 根据文章ID筛选推荐文章
        return getRecommendedOrRandomArticles(articles, recommendations);
    }

    private List<RelateDTO> createRating(List<WebRating> temp) {
        List<RelateDTO> data = Lists.newArrayList();
        temp.forEach(t -> {
            RelateDTO relateDTO = new RelateDTO();
            relateDTO.setUseId(t.getUserId().intValue());
            relateDTO.setItemId(t.getArticleId());
            relateDTO.setIndex(t.getRating());
            data.add(relateDTO);
        });
        return data;
    }


    @Override
    public List<WebArticle> selectBlogArticleList() {
        return   webArticleMapper.selectBlogArticleList();
    }

    @Override
    public List<WebArticle> selectBlogArticleHot() {
        return   webArticleMapper.selectBlogArticleHot();
    }

    @Override
    public List<WebArticle> selectBlogArticleByGroup(int groupId) {
        return webArticleMapper.selectBlogArticleByGroup(groupId);
    }

    @Override
    public WebArticle selectBlogArticleById(int id) {
        return webArticleMapper.selectBlogArticleById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult beginArticleRate(Long userId, int articleId, int rate) {
        try {
            webArticleMapper.updateOrInsertArticleRate(userId, articleId, rate);
        }catch (Exception e) {
             e.printStackTrace();
            return AjaxResult.error("评分失败");
        }
        return AjaxResult.success("评分成功");
    }




    @Override
    public int selectRate(int id) {
        return webArticleMapper.selectRateSvg(id);
    }

    /**
     * 获取编写的文章
     * @param userId
     * @return 文章列表
     */
    @Override
    public AjaxResult getMyArticle(Long userId) {
        // 根据用户id查询对应的文章
        List<WebArticle> articles = webArticleMapper.selectBlogArticleByAuthorId(userId);
        return AjaxResult.success(additional(articles));
    }

    /**
     * 获取我收藏的文章
     * @param userId
     * @return 文章列表
     */
    @Override
    public AjaxResult getMyCollectArticle(Long userId) {
        // 1. 优先从Redis获取已点赞文章ID
        Set<Long> articleIds =getLikedArticleIdsFromRedis(2,userId);
//      // 2. 缓存未命中时查数据库
        if (articleIds.isEmpty()) {
            articleIds = getLikedArticleIdsFromDB(2,userId);
            // 重建缓存
            rebuildRedisCache(2,userId, articleIds);
        }
        // 3. 转换为数组查询文章
        if (!articleIds.isEmpty()) {
            Long[] idArray = articleIds.toArray(new Long[0]);
            List<WebArticle> articles = webArticleMapper.selectArticleByStarArr(idArray);
            return AjaxResult.success(additional(articles));
        }
        return AjaxResult.success("无数据");
    }

    /**
     *   获取我点赞的文章
     * @param userId
     * @return 文章列表
     */
    @Override
    public AjaxResult getMyLikeArticle(Long userId) {
        // 1. 优先从Redis获取已点赞文章ID
        Set<Long> articleIds =getLikedArticleIdsFromRedis(1,userId);
//      // 2. 缓存未命中时查数据库
        if (articleIds.isEmpty()) {
            articleIds = getLikedArticleIdsFromDB(1,userId);
            // 重建缓存
            rebuildRedisCache(1,userId,articleIds);
        }
        // 3. 转换为数组查询文章
        if (!articleIds.isEmpty()) {
            Long[] idArray = articleIds.toArray(new Long[0]);
            List<WebArticle> articles = webArticleMapper.selectArticleByStarArr(idArray);
            return AjaxResult.success(additional(articles));
        }
        return AjaxResult.success("无数据");
    }

    private void rebuildRedisCache(Integer type,Long userId, Set<Long> articleIds) {
        String prefix = String.format("user:like:%d:%d:*", userId,type);
        articleIds.forEach(targetId -> {
            String key = prefix + targetId;
            redisTemplate.opsForValue().set(key, "1", 30, TimeUnit.DAYS);
        });
    }

    private Set<Long> getLikedArticleIdsFromDB(Integer type,Long userId) {
        List<Long> dbIds = webLIkeMapper.selectLikedTargetIds(
                userId,
                1, // targetType=1表示文章
                1  // status=1有效状态
        );
        return new HashSet<>(dbIds);
    }

    private Set<Long> getLikedArticleIdsFromRedis(Integer type,Long userId) {
        String pattern = String.format("user:like:%d:%d:*", userId,type);
        Set<String> keys = redisTemplate.keys(pattern);

        return keys.stream()
                .map(key -> {
                    String[] parts = key.split(":");
                    return Long.parseLong(parts[4]); // 解析targetId
                })
                .collect(Collectors.toSet());
    }


    @Override
    public List<WebArticle> additional(List<WebArticle> articles) {
        for(Article article:  articles){
            //根据文章id查询博客文章标签列表
            List<Label> labels =  blogLabelService.selectBlogLabelListById(article.getId());
            article.setLabels(labels);
            //查询点赞量
            article.setLikes( blogLikeService.getCount(1,article.getId()));
            //查询收藏量
            article.setStar(blogLikeService.getCount(2,article.getId()));
            // 查询访问量
            String arrViews = (String) redisTemplate.opsForHash().entries("article:views").get(String.valueOf(article.getId()));
            // 如果 arrViews 为 null 或空字符串，设置 views 为 0
            int views = 0;
            if (arrViews != null && !arrViews.trim().isEmpty()) {
                try {
                    views = Integer.parseInt(arrViews.trim()); // 转换为 int
                } catch (NumberFormatException e) {
                    // 如果 arrViews 不是有效的数字字符串，记录日志或处理异常
                    System.err.println("Invalid views value in Redis: " + arrViews);
                    views = 0; // 设置默认值
                }
            }
             // 设置文章的访问量
            article.setViews(views);
        }
        return articles;
    }

    @Override
    public void updateViewCount(int articleId) {
        // 1. 增加访问量（Redis原子操作）
        redisTemplate.opsForHash().increment("article:views", String.valueOf(articleId), 1);
    }
    protected List<WebArticle> getRecommendedOrRandomArticles(List<WebArticle> articles, List<Integer> recommendations) {
       final int size = 3;
       // 如果推荐文章为空，则补充随机三篇文章
       if (recommendations == null ||articles.size()<3  ){
           return  articles;
       }
        // 根据文章ID筛选推荐文章
        List<WebArticle> recommendedArticles = articles.stream()
                .filter(e -> recommendations.contains(e.getId().intValue()))
                .collect(Collectors.toList());

        // 如果推荐文章少于3条，则充补随机文章
        if (recommendedArticles.size() < size) {
            Random random = new Random();
            int count = size - recommendedArticles.size();

            // 过滤掉已经推荐的文章，避免重复
            List<WebArticle> remainingArticles = articles.stream()
                    .filter(e -> !recommendations.contains(e.getId().intValue()))
                    .collect(Collectors.toList());

            // 从剩余的文章中随机选择
            List<WebArticle> randomArticles = remainingArticles.stream()
                    .skip(random.nextInt(remainingArticles.size() - count + 1))
                    .limit(count)
                    .collect(Collectors.toList());

            // 合并推荐文章和随机文章
            return Stream.concat(recommendedArticles.stream(), randomArticles.stream())
                    .collect(Collectors.toList());
        } else {
            return recommendedArticles;
        }
    }
}